package com.wxy.ej.flow.modules.act.service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.rt.easyjava.common.base.PageDto;
import com.rt.easyjava.common.base.Result;
import com.rt.easyjava.common.base.UserDetailsDto;
import com.rt.easyjava.common.config.dozer.MyMapper;
import com.rt.easyjava.common.utils.others.DateUtils;
import com.rt.easyjava.common.utils.others.JsonUtils;
import com.rt.easyjava.common.utils.others.StringUtils;
import com.wxy.ej.flow.modules.act.bean.FormData;
import com.wxy.ej.flow.modules.act.dto.FormDataDto;
import com.wxy.ej.flow.modules.act.dto.ProcessDefinitionDto;
import com.wxy.ej.flow.modules.act.dto.TaskDto;
import com.wxy.ej.flow.modules.act.dto.TaskQueryDto;
import com.wxy.ej.flow.modules.act.service.beanService.ActivitiUtilsService;
import com.wxy.ej.flow.modules.act.service.beanService.FormDataService;
import com.wxy.ej.flow.modules.act.service.beanService.ActImageService;
import org.activiti.engine.*;
import org.activiti.engine.form.FormProperty;
import org.activiti.engine.form.StartFormData;
import org.activiti.engine.form.TaskFormData;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricTaskInstanceQuery;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestBody;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public class ActivitiService {
    @Resource private MyMapper mapper;
    @Resource private RuntimeService runtimeService;
    @Resource private RepositoryService repositoryService;
    @Resource private TaskService taskService;
    @Resource private FormService formService;
    @Resource private HistoryService historyService;
    @Resource private IdentityService identityService;
    @Resource private FormDataService formDataService;
    @Resource private ActImageService actImageService;
    @Resource private ActivitiUtilsService activitiUtilsService;
    @Resource HttpServletRequest request;
    /**
     * 获取启动流程上定义的动态表单
     * @param procDefKey
     * @return
     */
    public Result queryStartFormKey(String procDefKey){
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(procDefKey).latestVersion().singleResult();
        StartFormData startFormData = formService.getStartFormData(processDefinition.getId());
        String formKey = startFormData.getFormKey();
        return Result.builder(formKey).success();
    }

    /**
     * 启动流程 By ProcDefKey
     * @param processDefinitionDto
     * processDefinitionDto.getAssigneeUserId(): 如定时任务调用指定用户
     * @return
     */
    public Result startProcByProcDefKey(ProcessDefinitionDto processDefinitionDto){
        String procDefKey = processDefinitionDto.key;
        String formDataStr = processDefinitionDto.getFormData();
        //String userId = processDefinitionDto.getUserId();

        //取登录用户
        UserDetailsDto userDetailsDto = UserDetailsDto.getUserDetailsDto(request.getParameter("userDetailsJson"));
        if (userDetailsDto==null) {
            userDetailsDto = new UserDetailsDto();
            // ?????? timer: maybe exception
        }
        String userId = userDetailsDto.getUserId();

        // 取需要的数据，存入流程变量
        Map<String, Object> varMap = new HashMap<>();
        if (!StringUtils.isNull(formDataStr)) {
            varMap = JsonUtils.json2Map(formDataStr);
        }
        // 通过timer调用时，传这个
        if (!StringUtils.isNull(processDefinitionDto.getAssigneeUserId())){
            varMap.put("assigneeUserId", processDefinitionDto.getAssigneeUserId());
        }

        //设置办理人、候选人、候选组
        //vars.put("assigneeUserId", "admin");
        //vars.put("candidateUsers", "test1,test2");
        //vars.put("candidateGroups", "group1,group2");
        String pId = null;
        try {
            // 用来设置启动流程的人员ID，引擎会自动把用户ID保存到activiti:initiator中
            identityService.setAuthenticatedUserId(userId);
            //启动流程，提交表单
            ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(procDefKey, processDefinitionDto.getBusinessKey(), varMap);
            pId = processInstance.getId();
            //表单数据
            FormData formData = new FormData();
            if (!StringUtils.isNull(processDefinitionDto.getFormKey())) {
                formData.setTtfFormId(Long.parseLong(processDefinitionDto.getFormKey()));
            }
            formData.setNodeType("StartEvent");
            formData.setNodeName("开始");
            formData.setProcDefKey(procDefKey);
            formData.setDataJson(formDataStr);
            //用户信息
            formData.setCreatorId(Long.parseLong(userDetailsDto.getUserId()));
            formData.setCreator(userDetailsDto.getUserName());
            formData.setUpdatorId(Long.parseLong(userDetailsDto.getUserId()));
            formData.setUpdator(userDetailsDto.getUserName());
            formData.setCompanyId(Long.parseLong(userDetailsDto.getCompanyId()));
            formData.setProcInstId(pId);
            formData.setProcDefId(processInstance.getProcessDefinitionId());
            // save form data
            formDataService.save(formData);
            // 设置流程实例名字
            runtimeService.setProcessInstanceName(processInstance.getId(), processInstance.getProcessDefinitionName());

        } finally {
            identityService.setAuthenticatedUserId(null);
        }

        // 完成第一个任务 （提交数据）
        //Task task = taskService.createTaskQuery().processInstanceId(pId).singleResult();
        //taskService.claim(task.getId(), userId); // 认领任务（对应的是在组任务，即从组任务中领取任务）
        //taskService.complete(task.getId(), vars);

        return Result.builder(pId).success("流程已启动");
    }


    /**
     * 完成任务 by TaskId
     */
    public Result completeByTaskId(TaskDto taskDto){
        String taskId = taskDto.getId();
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
        String nodeName = task.getName();
        String formDataStr = taskDto.getFormData();

        //取登录用户
        UserDetailsDto userDetailsDto = UserDetailsDto.getUserDetailsDto(request.getParameter("userDetailsJson"));
        FormData formData = new FormData();
        if (!StringUtils.isNull(task.getFormKey())) {
            formData.setTtfFormId(Long.parseLong(task.getFormKey()));
        }
        formData.setNodeType("UserTask");
        formData.setNodeName(nodeName);
        formData.setDataJson(formDataStr);
        formData.setTaskId(taskDto.getId());
        formData.setProcInstId(task.getProcessInstanceId());
        formData.setProcDefKey(pi.getProcessDefinitionKey());
        formData.setProcDefId(pi.getProcessDefinitionId());
        //用户信息
        formData.setCreatorId(Long.parseLong(userDetailsDto.getUserId()));
        formData.setCreator(userDetailsDto.getUserName());
        formData.setUpdatorId(Long.parseLong(userDetailsDto.getUserId()));
        formData.setUpdator(userDetailsDto.getUserName());
        formData.setCompanyId(Long.parseLong(userDetailsDto.getCompanyId()));
        formData.setApproval(taskDto.getApproval());
        formDataService.save(formData);

        // complete task
        // 取需要的数据，存入流程变量
        Map<String, Object> varMap = JsonUtils.json2Map(formDataStr);
        varMap.put("approval", taskDto.getApproval()==null||taskDto.getApproval().intValue()==0?false:true);
        System.out.println("taskService.complete");
        taskService.complete(taskId, varMap);

        return Result.builder().success();
    }


    /**
     * 我发起的流程
     * @param taskQueryDto
     *  status: 1, 执行中；2，已完成
     * @return
     */
    public Result listStartProcInstByUserId(TaskQueryDto taskQueryDto) {
        //取登录用户
        UserDetailsDto userDetailsDto = UserDetailsDto.getUserDetailsDto(request.getParameter("userDetailsJson"));
        if (userDetailsDto==null) {
            userDetailsDto = new UserDetailsDto();
            // ?????? timer: maybe exception
        }
        String userId = userDetailsDto.getUserId();

        switch (taskQueryDto.getStatus()) {
            case 1:{
                return activitiUtilsService.listMyStartProcInstRunningByUserId(taskQueryDto, userId);
            }
            case 2:{
                return activitiUtilsService.listMyStartProcInstHisByUserId(taskQueryDto, userId);
            }
        }
        return Result.builder().fault("请检查查询条件");
    }

    /**
     * 我的待办任务列表
     * @param pageDto
     * @return
     */
    public Result listWaitingTaskByLoginUserId(PageDto pageDto) {
        //取登录用户
        UserDetailsDto userDetailsDto = UserDetailsDto.getUserDetailsDto(request.getParameter("userDetailsJson"));
        if (userDetailsDto==null) {
            userDetailsDto = new UserDetailsDto();
            // ?????? timer: maybe exception
        }
        String userId = userDetailsDto.getUserId();

        TaskQuery taskQuery = taskService.createTaskQuery().taskCandidateOrAssigned(userId)
                .active();
        List<Task> listTask = taskQuery.orderByTaskCreateTime().desc()
                .listPage((pageDto.getPage()-1)*pageDto.getLimit(), pageDto.getLimit());
        int total =  (int)taskQuery.count();

        List<Map<String, Object>> list = new ArrayList<>();
        for (Task task : listTask) {
            // 取 start from properties
            StartFormData startFormData = formService.getStartFormData(task.getProcessDefinitionId());
            List<FormProperty> formProperties = startFormData.getFormProperties();
            JSONArray formDataArr = new JSONArray();
            for (FormProperty formProperty: formProperties) {
                String formPropId = formProperty.getId(); // eg: id, days, reasons ...
                Object formPropValue = runtimeService.getVariable(task.getProcessInstanceId(), formPropId);
                JSONObject formData = new JSONObject();
                formData.put("id", formPropId);
                formData.put("name", formProperty.getName());
                formData.put("value", formPropValue);
                formDataArr.add(formData);
            }

            ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
            Map<String, Object> map = new HashMap<>();
            map.put("formDataArr", formDataArr);
            map.put("taskId", task.getId());
            map.put("taskName", task.getName());
            map.put("taskDescription", task.getDescription());
            map.put("taskOwner", task.getOwner());
            map.put("procInstId", pi.getId());
            map.put("procInstName", pi.getName());
            map.put("procInstStartUserId", pi.getStartUserId());
            map.put("procInstStartTime", DateUtils.formatDate(pi.getStartTime(),"yyyy-MM-dd HH:mm:ss"));
            list.add(map);
        }
        return Result.builder(list).setTotal(total).success();
    }

    /**
     * 我完成的任务
     * @param pageDto
     * @return
     */
    public Result listMyCompleteTaskByLoginUserId(PageDto pageDto) {
        //取登录用户
        UserDetailsDto userDetailsDto = UserDetailsDto.getUserDetailsDto(request.getParameter("userDetailsJson"));
        if (userDetailsDto==null) {
            userDetailsDto = new UserDetailsDto();
            // ?????? timer: maybe exception
        }
        String userId = userDetailsDto.getUserId();

        HistoricTaskInstanceQuery historicTaskInstanceQuery = historyService.createHistoricTaskInstanceQuery()
                .taskAssignee(userId)
                .finished();

        List<HistoricTaskInstance> historicTaskInstanceList = historicTaskInstanceQuery
                        .orderByTaskCreateTime()
                        .desc().listPage((pageDto.getPage()-1)*pageDto.getLimit(),pageDto.getLimit());
        int total =  (int)historicTaskInstanceQuery.count();

        List<Map<String, Object>> list = new ArrayList<>();
        for (HistoricTaskInstance task : historicTaskInstanceList) {
            HistoricProcessInstance pi = historyService.createHistoricProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
            Map<String, Object> map = new HashMap<>();
            map.put("taskId", task.getId());
            map.put("taskName", task.getName());
            map.put("taskDescription", task.getDescription());
            map.put("taskOwner", task.getOwner());
            map.put("procInstId", pi.getId());
            map.put("procInstName", pi.getName());
            map.put("procInstStartUserId", pi.getStartUserId());
            map.put("procInstStartTime", DateUtils.formatDate(pi.getStartTime(),"yyyy-MM-dd HH:mm:ss"));
            map.put("isProcessEnd", isProcessEnd(pi.getId()));
            map.put("processStatus", isProcessEnd(pi.getId())?"已结束":"未结束");
            list.add(map);
        }


        return Result.builder(list).setTotal(total).success();
    }

    /**
     * 根据流程实例Id,获取实时流程图片
     * @param procInstId
     * @return
     */
    public Result getFlowImgByProcInstId(String procInstId) {
        String imgBase64 = actImageService.getFlowImgByInstanceId(procInstId);
        return Result.builder(imgBase64);
    }


    /**
     * 分配任务 By TaskId processInstanceId
     * @param processInstanceId
     * @param userId
     * @return
     */
    public Result assignTaskToUserByProcInstId(String processInstanceId, String userId){
        Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
        taskService.setAssignee(task.getId(), userId);
        return Result.builder(task.getId()).success("Assign taskId:"+task.getId()+" to userId:"+userId);
    }

    /**
     * 分配任务 By TaskId
     * @param taskId
     * @param userId
     * @return
     */
    public Result assignTaskToUserByTaskId(String taskId, String userId){
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        taskService.setAssignee(task.getId(), userId);
        return Result.builder(task.getId()).success("Assign taskId:"+task.getId()+" to userId:"+userId);
    }


    public boolean isProcessEnd(String processInstanceId) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if (processInstance != null) {
            return processInstance.isEnded();
        }
        return true;
    }


    /**
     * 页面UI渲染，和数据Laod
     * @param taskId
     * @param isHisTask: 0:已完成任务；1：执行中的任务
     * @return
     */
    public Result queryFormKeyList(String taskId, int isHisTask){
        String procInstId = "";
        if (isHisTask==0) {
            Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
            procInstId = task.getProcessInstanceId();
        }else{
            HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult();
            procInstId = historicTaskInstance.getProcessInstanceId();
        }
        // 历史数据
        List<FormData> hisFormDataList = formDataService.selectListByKeyAndOrder("proc_inst_id", procInstId, true);
        List<FormDataDto> hisFormDataDtoList = mapper.maplist(hisFormDataList, FormDataDto.class);

        // 取任务节点表单数据;
        String taskFormKey = "";
        if (isHisTask==0) {
            TaskFormData taskFormData = formService.getTaskFormData(taskId);
            taskFormKey = taskFormData.getFormKey();
        }

        return Result.builder(hisFormDataDtoList).setRemark(taskFormKey).success();
    }

    public Result queryFormKeyListByProcInstId(String procInstId){
        List<FormData> hisFormDataList = formDataService.selectListByKeyAndOrder("proc_inst_id", procInstId, true);
        List<FormDataDto> hisFormDataDtoList = mapper.maplist(hisFormDataList, FormDataDto.class);


        return Result.builder(hisFormDataDtoList).success();
    }




}
